package algorithm;

import java.util.ArrayList;

import structure.*;

public class utility {

	public utility(){
		
	}
	
	//restituisce tutti i nullable che ci sono nelle produzioni di una grammatica
	public static ArrayList<String> getNullable(ArrayList<production> productions){
		
		ArrayList<production> productionsCopy= new ArrayList<production>();
		productionsCopy= miscellaneous.cloneArray(productions);
		ArrayList<String> nullable= new ArrayList<String>();
		ArrayList<String> allLeftside= utility.getAllLeftSide(productionsCopy);
		boolean stop= false;
		
		while(!stop){
			
			ArrayList<Boolean> check= new ArrayList<Boolean>();
			
			for(production p : productionsCopy){
				
				ArrayList<String> rightside= p.getRightSide();
				ArrayList<String> rightsideNew= new ArrayList<String>();
				ArrayList<ArrayList<String>> matrixRightside= new ArrayList<ArrayList<String>>();
				ArrayList<Boolean> innerCheck= new ArrayList<Boolean>();
				boolean modified= false;
				
				for(String s : rightside){
					
					if(s.length() == 1 && allLeftside.contains(s)){
						
						//faccio l'unfold di s
						production tmpP= getProductionByLeftSide(s, productionsCopy);
						matrixRightside.add(singleUnfold(rightside, s, tmpP.getRightSide()));
						innerCheck.add(true);
					}
					else if(s.length() == 1 && s.equals("e")){
						ArrayList<String> tmpAR= new ArrayList<String>();
						tmpAR.add(s);
						matrixRightside.add(tmpAR);
						innerCheck.add(true);
					}
					else
						innerCheck.add(false);
				}
				
				for(boolean b : innerCheck){
					
					if(b==true){
						
						modified= true;
					}
				}
				//anche se è stata fatta una sola modifica devo aggiornare il rightside
				if(modified){
					
					for(ArrayList<String> ar : matrixRightside){
						
						for(String s : ar){
							
							if(!rightsideNew.contains(s)){
								
								rightsideNew.add(s);
							}
						}
					}
					//aggiorno il rightside
					p.setRightSide(rightsideNew);
					check.add(true);
				}
				else
					check.add(false);
			}
			
			for(boolean b : check){
				
				if(b==true){
					
					stop= true;
				}
			}
		}
		
		//dopo l'unfolding aggiungo i nullable all'array
		for(production p : productionsCopy){
			
			if(p.getRightSide().contains("e")){
				
				nullable.add(p.getLeftSide());
			}
		}
		
		return nullable;
	}
	
	//controlla che vi siano e-production
	public static boolean isThereEproduction(ArrayList<production> production){
		
		ArrayList<String> allRighside= new ArrayList<String>();
		
		for(production p : production){
			
			allRighside.addAll(p.getRightSide());
		}
		
		return allRighside.contains("e");
	}
	
	//crea le combinazioni dei caratteri di una stringa
	
	//restituisce tutte le stringhe che sono leftside
	public static ArrayList<String> getAllLeftSide(ArrayList<production> paramP){
		
		ArrayList<String> toReturn= new ArrayList<String>();
		for (production p :paramP){
			
			toReturn.add(p.getLeftSide());
		}
		
		return toReturn;
	}
	
	//restituisce la produzione in base al suo leftside
	public static production getProductionByLeftSide(String leftSide, ArrayList<production> paramP){
		
		for (production p : paramP){
			
			if(leftSide.equals(p.getLeftSide())){
				
				return p;
			}
		}
		
		return null;
	}
	
	//controlla se la produzione è trivial unit, e la elimina in caso affermativo
	public static void removeTrivialUnit(production p){
		/*
		 * una trivial unit è una produzione A->A
		 */
		ArrayList<String> rightside= p.getRightSide();
		ArrayList<String> rightsideNew= new ArrayList<String>();
		for(String s : rightside){
			
			if(!s.equals(p.getLeftSide())){
				
				rightsideNew.add(s);
			}
		}
		
		p.setRightSide(rightsideNew);
	}
	
	//rimpiazza un non terminale con le sue produzioni, se ce ne sono
	public static void replaceProduction(String left, String right, ArrayList<production> productions ){

		if(getAllLeftSide(productions).contains(left) && getAllLeftSide(productions).contains(right)){
			
			production leftProduction= utility.getProductionByLeftSide(left, productions);
			ArrayList<String> rightsideProductionLeft= leftProduction.getRightSide();
			ArrayList<String> rightsideProductionRight= utility.getProductionByLeftSide(right, productions).getRightSide();
			ArrayList<String> rightSideNew= new ArrayList<String>();
			
			for(int i=0; i<rightsideProductionLeft.size(); i++){
				
				if(right.equals(rightsideProductionLeft.get(i))){
					
					rightsideProductionLeft.remove(i);
					//opero la sostituzione delle produzioni
					for(int j=0; j<rightsideProductionRight.size(); j++){
						
						if(!rightsideProductionLeft.contains(rightsideProductionRight.get(j)) &&
								!rightSideNew.contains(rightsideProductionRight.get(j)))
							rightSideNew.add(rightsideProductionRight.get(j));
					}
					
					utility.removeTrivialUnit(leftProduction);
					break;
				}
			}
			leftProduction.addRightSide(rightSideNew);
		}
		else{
			//impossibile ma non si sa mai
			System.out.println("there aren't production to replace or to be replaced"); 
		}
	}
	
	//eseguo l'unfold delle produzioni unitarie es: S->A
	public static ArrayList<String> singleUnfold(ArrayList<String> rightside, String toBeUnfold, ArrayList<String> rightsideToUnfold){
		
		int position= miscellaneous.getPositionOfString(rightside, toBeUnfold);
		ArrayList<String> toReturn= new ArrayList<String>();
		toReturn.addAll(rightside);
		toReturn.remove(position);
		toReturn.addAll(rightsideToUnfold);
		return toReturn;
	}
	
	//controlla se l'item passato è un complete item
	public static boolean isComplete(item i){
		
		String right= i.getRightside();
		
		return right.endsWith(".");
	}
	
	//controlla che l'assioma non compaia nei rightside di altre produzioni
}
